﻿/**************************************************************************
**
**  MisCip
**  ~~~~~~~~~~
**
**  Commands.cs: 
**  ---------
**  Führt die einzelnen Befehle aus
**
**  
**************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;


namespace Pic_Simulator
{
    public partial class MisCip
    {
        public void scanCommand(string aCmd, int bCmd)
        {
            byte d = 0;     // Destination Bit => default 0
            byte f;         // Register File Address
            byte b;         // Bit Adresse mit 8-Bit File Register
            byte k;         // literal feld
            short kBig;     // großes literal Feld für call und goto

            if ((bCmd & 0x80) == 0x80)  //Destination select
            {
                d = 1;
            }

            f = (byte)(bCmd & 0x7F);    //Registeradresse aus Befehl holen

            b = (byte)(1 << ((bCmd & 0x380) >> 7)); //bytestelle aus Befehl holen

            k = (byte)(bCmd & 0xFF);    //Literal aus Befehl holen

            kBig = (byte)(bCmd & 0x7FF); //Literal für GOTO und CALL

            switch (aCmd)   //Den entsprechenden Befehl auswählen
            {
                //Byte orientierte File Register Operationen
                case "ADDWF":
                    opADDWF(d, f);
                    break;
                case "ANDWF":
                    opANDWF(d, f);
                    break;
                case "CLRF":
                    opCLRF(f);
                    break;
                case "CLRW":
                    opCLRW();
                    break;
                case "COMF":
                    opCOMF(d, f);
                    break;
                case "DECF":
                    opDECF(d, f);
                    break;
                case "DECFSZ":
                    opDECFSZ(d, f);
                    break;
                case "INCF":
                    opINCF(d, f);
                    break;
                case "INCFSZ":
                    opINCFSZ(d, f);
                    break;
                case "IORWF":
                    opIORWF(d, f);
                    break;
                case "MOVF":
                    opMOVF(d, f);
                    break;
                case "MOVWF":
                    opMOVWF(f);
                    break;
                case "NOP":
                    opNOP();
                    break;
                case "RLF":
                    opRLF(d, f);
                    break;
                case "RRF":
                    opRRF(d, f);
                    break;
                case "SUBWF":
                    opSUBWF(d, f);
                    break;
                case "SWAPF":
                    opSWAPF(d, f);
                    break;
                case "XORWF":
                    opXORWF(d, f);
                    break;
                //Bit orientierte File Register Operationen
                case "BCF":
                    opBCF(b, f);
                    break;
                case "BSF":
                    opBSF(b, f);
                    break;
                case "BTFSC":
                    opBTFSC(b, f);
                    break;
                case "BTFSS":
                    opBTFSS(b, f);
                    break;
                //Literal und Control Operationen
                case "ADDLW":
                    opADDLW(k);
                    break;
                case "ANDLW":
                    opANDLW(k);
                    break;
                case "CALL":
                    opCALL(kBig);
                    break;
                case "CLRWDT":
                    opCLRWDT();
                    break;
                case "GOTO":
                    opGOTO(kBig);
                    break;
                case "IORLW":
                    opIORLW(k);
                    break;
                case "MOVLW":
                    opMOVLW(k);
                    break;
                case "RETLW":
                    opRETLW(k);
                    break;
                case "RETFIE":
                    opRETFIE();
                    break;
                case "RETURN":
                    opRETURN();
                    break;
                case "SLEEP":
                    opSLEEP();
                    break;
                case "SUBLW":
                    opSUBLW(k);
                    break;
                case "XORLW":
                    opXORLW(k);
                    break;
                //Falsche oder andere Eingabe
                default:
                    break;
            }
        }
        private void opADDWF(byte d, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung?
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            int help = iWReg + iReg[f]; //Die Addition in einer Hilfsvariablen ausführen

            if (help > 255) //Überlauf?
            {
                iReg[0x03] |= 0x01;//C=1;
                help = help % 256; //Wertkorrektur
            }
            else
            {
                iReg[0x03] &= 0xFE;  //C=0;
            }
            //Ergebnis größer als 0xf? Dann DC setzen
            if (help >= 0x10)
            {
                iReg[0x03] |= 0x02; //DC = 1;
            }
            else
            {
                iReg[0x03] &= 0xFD;  //DC=0;
            }
            if (help == 0) // Ergebnis == 0?//Zero-Bit setzen
            {
                iReg[0x03] |= 0x04; //Z=1;
            }
            else
            {
                iReg[0x03] &= 0xFB; //Z=0;
            }
            if (d == 0)     //Entweder in W oder in f Speichern!
                iWReg = help;
            else
                iReg[f] = help;
            lCycles++;
            iPC++;
        }
        private void opANDWF(byte d, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            int help = (iWReg & iReg[f]) & 0xff;    //Hilfsvariable
            if (help == 0)  //Ergebnis == 0? Zerobit setzen
            {
                iReg[0x03] |= 0x04; //Z = 1;
            }
            else
            {
                iReg[0x03] &= 0xFB; //Z=0;
            }
            if (d == 0) //Entweder in W oder f speichern
                iWReg = help;
            else
                iReg[f] = help;
            lCycles++;
            iPC++;
        }
        private void opCLRF(byte f)    //Wenn ich das Statusregister cleare wird ja das Z-Bit trotzdem gesetzt im Statusreg oder?
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            iReg[f] = 0x00; //Register clearen
            iReg[0x03] |= 0x04; //Z=1;
            iPC++;
        }
        private void opCLRW()
        {
            iWReg = 0x00; //W-Reg clearen
            iReg[0x03] |= 0x04; //Z=1;
            lCycles++;
            iPC++;
        }
        private void opCOMF(byte d, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            int help = iReg[f] ^ 0xFF;  //Komplement bilden (8bit)!
            if (help == 0)  //Ergebnis == 0?
            {
                iReg[0x03] |= 0x04; //Z=1;
            }
            else
            {
                iReg[0x03] &= 0xFB;  //Z=0;
            }
            if (d == 0) //Entweder in W oder f speichern
                iWReg = help;
            else
                iReg[f] = help;
            lCycles++;
            iPC++;
        }
        private void opDECF(byte d, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            int help = iReg[f] - 1; //Hilfsvariable

            if (help < 0) //Überlauf?
            {
                help = 256 + help; //Überlauf
            }
            if (help == 0)
            {
                iReg[0x03] |= 0x04;  //Z=1;
            }
            else
            {
                iReg[0x03] &= 0xFB;  //Z=0;
            }
            if (d == 0)   //Entweder in W oder f speichern
                iWReg = help;
            else
                iReg[f] = help;
            lCycles++;
            iPC++;
        }
        private void opDECFSZ(byte d, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            int help = iReg[f] - 1;  //Hilfsvariable
            if (help < 0)   //Überlauf?
                help = 256 + help;

            if (d == 0)     //Entweder in W oder f speichern
                iWReg = help;
            else
                iReg[f] = help;
            if (help == 0)
            {
                //NOP!
                lCycles += 2;
                iPC += 2;
            }
            else
            {
                lCycles++;
                iPC++;
            }
        }
        private void opINCF(byte d, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            int help = iReg[f] + 1;  //Hilfsvariable
            if (help > 255)     //Überlauf?
                help = help % 256;
            if (help == 0)
            {
                iReg[0x03] |= 0x04;  //Z=1;
            }
            else
            {
                iReg[0x03] &= 0xFB;  //Z=0;
            }
            if (d == 0)    //Entweder in W oder f speichern
                iWReg = help; 
            else
                iReg[f] = help;  
            lCycles++;
            iPC++;
        }
        private void opINCFSZ(byte d, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            int help = iReg[f] + 1;  //Hilfsvariable
            if (help > 255) //Überlauf
                help = help % 256;
            
            if (d == 0) //Entweder in W oder f speichern
                iWReg = help;
            else
                iReg[f] = help;
            if (help == 0)
            {
                //NOP!
                lCycles += 2;
                iPC += 2; //Nächsten Befehl überspringen
            }
            else
            {
                lCycles++;
                iPC++;  //Befehl nich überspringen
            }
        }
        private void opIORWF(byte d, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            int help = iWReg | iReg[f];  //Hilfsvariable
            if (help == 0)  //Achtung hier Z negiert!
            {
                iReg[0x03] &= 0xFB;   //Z=0;  
            }
            else
            {
                iReg[0x03] |= 0x04;   //Z=1;
            }
            if (d == 0) //Entweder in W oder f speichern
            {
                iWReg = help;
            }
            else
            {
                iReg[f] = help;
            }
            lCycles++;
            iPC++;
        }
        private void opMOVF(byte d, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            int help = iReg[f];  //Hilfsvariable
            if (help == 0)
            {
                iReg[0x03] |= 0x04;  //Z=1;
            }
            else
            {
                iReg[0x03] &= 0xFB;   //Z=0;
            }
            if (d == 0)    //Entweder in W oder f speichern
                iWReg = help;
            else
                iReg[f] = help;
            lCycles++;
            iPC++;
        }
        private void opMOVWF(byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            iReg[f] = iWReg;   //In f speichern
            lCycles++;
            iPC++;
        }
        private void opNOP()
        {
            lCycles++;
            iPC++;
        }
        private void opRLF(byte d, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            int help = iReg[f] << 1;    //Hilfsvariable
            if (help > 255) //Überlauf?
            {
                iReg[0x03] |= 0x01; //C=1;
                help &= 0xff;   //nur 8Bit behalten!
            }
            else
            {
                iReg[0x03] &= 0xFE; //C=0;
            }
            if (d == 0) //Entweder in W oder f speichern
            {
                iWReg = help;
            }
            else
            {
                iReg[f] = help;
            }
            lCycles++;
            iPC++;
        }
        private void opRRF(byte d, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            int help = iReg[f] >> 1;  //Hilfsvariable
            if (help < 0)      //"Unterlauf"
            {
                iReg[0x03] |= 0x01;//C=1;
                help &= 0xff;   //nur 8Bit behalten!
            }
            else
            {
                iReg[0x03] &= 0xFE; //C=0;
            }
            if (d == 0) //Entweder in W oder f speichern
            {
                iWReg = help;
            }
            else
            {
                iReg[f] = help;
            }
            lCycles++;
            iPC++;
        }
        private void opSUBWF(byte d, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            int help = iReg[f] - iWReg;  //Hilfsvariable
            if (help < 0)
            {
                iReg[0x03] &= 0xFE; //C=0;
                iReg[0x03] &= 0xFB; //Z = 0; //DC wird nach korrektur bestimmt
                help = 256 + help;  //Überlauf
            }
            else
            {
                iReg[0x03] |= 0x01; //C=1;
            }
            if (help == 0)
            {
                iReg[0x03] |= 0x04; //Z=1;
                iReg[0x03] |= 0x01; //C=1;
            }
            else
            {
                iReg[0x03] &= 0xFB;   //Z=0;
                iReg[0x03] &= 0xFE;  //C=0;
            }

            if (help >= 0x10)
            {
                iReg[0x03] |= 0x02; //DC=1;
            }
            else
            {
                iReg[0x03] &= 0xFD; //DC=0;
            }
            if (d == 0) //Entweder in W oder f speichern
            {
                iWReg = help;
            }
            else
            {
                iReg[f] = help;
            }
            lCycles++;
            iPC++;
        }
        private void opSWAPF(byte d, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check
            //2 Hilfsvariablen anlegen
            int helpn1 = iReg[f];
            helpn1 = (helpn1 << 4) & 0xF0;  //Hohes Nibble zu niedrigem Nibble 
            int helpn2 = iReg[f];
            helpn2 = (helpn2 >> 4) & 0x0F;  //Niedriges Nibble zu hohes Nibble 

            if (d == 0) //Entweder in W oder f speichern
                iWReg = helpn1 | helpn2;
            else
                iReg[f] = helpn1 | helpn2;
            lCycles++;
            iPC++;
        }
        private void opXORWF(byte d, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            int help = iWReg ^ iReg[f];   //Hilfsvariable
            if (help == 0)
            {
                iReg[0x03] |= 0x04;  //Z=1;
            }
            else
            {
                iReg[0x03] &= 0xFB;  //Z=0;
            }
            if (d == 0)    //Entweder in W oder f speichern
                iWReg = help;
            else
                iReg[f] = help;
            lCycles++;
            iPC++;
        }

        private void opBCF(byte b, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            iReg[f] = iReg[f] & (~b);    //lösche bit
            lCycles++;
            iPC++;
        }
        private void opBSF(byte b, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            iReg[f] = iReg[f] | b;      //setze bit
            lCycles++;
            iPC++;
        }
        private void opBTFSC(byte b, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            if ((iReg[f] & b) > 0)  //Bit gesetzt?
            {
                lCycles++;
                iPC++;
            }
            else
            {
                //NOP
                lCycles += 2;
                iPC += 2;
            }
        }
        private void opBTFSS(byte b, byte f)
        {
            if (f == 0x00) f = (byte)iReg[0x04];  //Indirekte Adressierung
            if ((iReg[0x03] & 0x20) > 0) f += 0x80;  //Bank 1 check

            if ((iReg[f] & b) > 0) //Bit gesetzt?
            {
                //NOP
                lCycles += 2;
                iPC += 2;
            }
            else
            {
                lCycles++;
                iPC++;
            }
        }

        private void opADDLW(byte k)
        {
            int help = iWReg + k;   //Hilfsvariable

            if (help >= 0x10)  //Ergebnis größer als 0xf? Dann DC setzen
            {
                iReg[0x03] |= 0x02; //DC = 1;
            }
            else
            {
                iReg[0x03] &= 0xFD; //DC=0;
            }
            if (help > 255) //Überlauf? C setzen
            {
                iReg[0x03] |= 0x01; //C=1;
                help = help % 256;  //Wertkorrektur
            }
            else
            {
                iReg[0x03] &= 0xFE;  //C=0;
            }
            if (help == 0) // Ergebnis == 0?Zero-Bit setzen
            {
                iReg[0x03] |= 0x04;  //Z=1;
            }
            else
            {
                iReg[0x03] &= 0xFB;   //Z=0;
            }
            if (help > 255) 
                help = help % 256;    //Überlauf
            iWReg = help;
            lCycles++;
            iPC++;
        }
        private void opANDLW(byte k)
        {
            iWReg = iWReg & k;  //Verundung
            if (iWReg == 0)
            {
                iReg[0x03] |= 0x04;  //Z=1;
            }
            else
            {
                iReg[0x03] &= 0xFB;   //Z=0;
            }
            lCycles++;
            iPC++;
        }
        private void opCALL(short k)
        {
            stack.Push(iPC + 1); //Rücksprungadresse merken!
            lCycles += 2;
            iPC = k;    //Springe zu Adresse
        }
        private void opCLRWDT()
        {
            lCycles++;
            lWatchdog = 0; //Watchdog clearen
        }
        private void opGOTO(short k)
        {
            lCycles += 2;
            iPC = k;    //Springe zu Adresse
        }

        private void opIORLW(byte k)
        {
            iWReg = iWReg | k;  //Veroderung
            if (iWReg == 0)
                iReg[0x03] |= 0x04;   // Z=1; im Datenblatt !?!?
            else
                iReg[0x03] &= 0xFB;   // Z=0;
            lCycles++;
            iPC++;
        }
        private void opMOVLW(byte k)
        {
            iWReg = k;  //Literal überschreibt W
            lCycles++;
            iPC++;
        }
        private void opRETFIE()
        {
            lCycles += 2;
            iPC = stack.Pop();  //Return an gemerkte Stelle
            iReg[0x0B] |= 0x80; //Interrupts wieder ermöglichen
            iReg[0x8B] |= 0x80;
        }
        private void opRETLW(byte k)
        {
            lCycles += 2;
            iWReg = k;  // Literal überschreibt W
            iPC = stack.Pop();  //Return an gemerkte Stelle
        }
        private void opRETURN()
        {
            lCycles += 2;
            iPC = stack.Pop();  //Return an gemerkte Stelle
        }
        private void opSLEEP()
        { 
            lWatchdog = 0; //Watchdog clearen
            timer0enabled = false;   //Timer0 deaktivieren
            sleepMode = true; //Simulator in Sleep Modus versetzen
            iPC++;
            iReg[0x03] |= 0x10; iReg[0x03] &= 0xF7;     //TO setzen und PD clearen
            iReg[0x83] |= 0x10; iReg[0x83] &= 0xF7;
            //Simulator kann dann durch Interrupt an RB0 oder PortB Bit 4-7 oder EEPROM-Write geweckt werden
        }
        private void opSUBLW(byte k)
        {
            int help = k - iWReg;   //Hilfsvariable
            if (help < 0)       //Überlauf?
            {
                iReg[0x03] &= 0xFE; //C=0;
                iReg[0x03] &= 0xFB; //Z = 0; //DC=??
                help = 256 + help;  //Überlauf
            }
            else
            {
                iReg[0x03] |= 0x01; //C=1;
            }
            if (help == 0)  //Ergebnis == 0?
            {
                iReg[0x03] |= 0x04;  //Z=1;
                iReg[0x03] |= 0x01;  //C=1;
            }
            else
            {
                iReg[0x03] &= 0xFB; //Z=0;
                iReg[0x03] &= 0xFE;  //C=0;
            }
            if (help >= 0x10)   //Ergebnis > 0x0F?
            {
                iReg[0x03] |= 0x02; //DC=1;
            }
            else
            {
                iReg[0x03] &= 0xFD; //DC=0;
            }
            iWReg = help;
            lCycles++;
            iPC++;
        }
        private void opXORLW(byte k)
        {
            iWReg = iWReg ^ k;  //Exklusiv-Oder
            if (iWReg == 0)
                iReg[0x03] |= 0x04; //Z=1;
            else
                iReg[0x03] &= 0xFB; //Z=0;
            lCycles++;
            iPC++;
        }
    }
}
